var DroppableInfo = function(droppable)
{
	this.droppable = droppable;
	this.pos = GetBodyPos(droppable.parentNode);

	this.test = function(x, y)
	{
		return (y > this.pos.top && 
			y < this.pos.bottom && 
			x > this.pos.left &&
			x < this.pos.right );
	}
}

var DragInst = null;
var DragHandler = function() 
{
	this.dragging = false;
	this.draggable = null;
	this.dragdiv = null;

	this.regContainer = null;
	this.regDroppable = null;

	this.lastX = 0;		// last mouse position
	this.lastY = 0;
	this.lastLeft = 0;	// last DIV position
	this.lastTop  = 0;

	this.droppableTable = [];

	this.Init = function()
	{
		var tester = jEnv.containerTag.replace(/\-/g, "\\-");
		DragInst.regContainer = new RegExp("(^|\\s)" + tester + "(\\s|$)", "i");

		tester = jEnv.dropTag.replace(/\-/g, "\\-");
		DragInst.regDroppable = new RegExp("(^|\\s)" + tester + "(\\s|$)", "i");

		//collect droppables
		DragInst.RefreshDroppableTable();
		
		document.onmousemove = DragInst.Moving;
		document.onmouseup	 = DragInst.End;
	}

	this.RefreshDroppableTable = function()
	{
		DragInst.droppableTable = [];
		var collection = document.getElementsByClassName(jEnv.dropTag);
		for( var i = 0; i < collection.length; i++)
		{
			info = new DroppableInfo(collection[i]);
			DragInst.droppableTable.push(info);

			//collection[i].addEventListener("mouseout", onKillFocusDroppable, true);
		}
	}

	this.FilterNeighborhood = function(draggable, droppable)
	{
		if(droppable == null || draggable == null) return false;

		// draggable and droppable shoud not be neighbors
		var preNode = PreSibling(draggable);
		if(preNode == droppable) return false;

		// their parent nodes should be neighbors neither
		var parent = PreSibling(droppable.parentNode);
		if(parent == draggable.parentNode) return false;

		return true;
	}

	this.Start = function(event)
	{
		// check event validity
		var e = event ? event : ( window.event ? window.event : undefined);
		if( e == undefined || e.target == undefined )
			return;
		
		// check the draggable
		var draggable = e.target.parentNode;
		if(!DragInst.regContainer.test(draggable.className))
		{
			return;
		}

		DragInst.draggable = draggable;
		DragInst.draggable.style.zIndex = 100;
		DragInst.draggable.style.opacity = 1.0;

		// event.x/clientX: browser client position
		// event.screenX: monitor screen position
		// event.offsetX: DIV internal position
		
		// create 
		var bodyPos = GetBodyPos(draggable);

		var style = document.defaultView.getComputedStyle(draggable);
		
		var txt = style.getPropertyValue("margin-left");
		var marginL = parseInt(txt);
		
		txt = style.getPropertyValue("margin-right");
		var marginR = parseInt(txt);
		
		txt = style.getPropertyValue("margin-top");
		var marginT = parseInt(txt);
		
		txt = style.getPropertyValue("margin-bottom");
		var marginB = parseInt(txt);
		
		DragInst.dragdiv = document.createElement("div");
		DragInst.dragdiv.innerHTML = draggable.outerHTML;
		DragInst.dragdiv.style.display = "block";
		DragInst.dragdiv.style.position= "absolute";
		DragInst.dragdiv.style.opacity = "0.8";
		DragInst.dragdiv.style.cursor  = "move";
		//DragInst.dragdiv.style.border  = "1px solid #FF0000";
		DragInst.dragdiv.style.width   = draggable.offsetWidth + marginL + marginR;
		DragInst.dragdiv.style.height	= draggable.offsetHeight + marginT + marginB;
		DragInst.dragdiv.style.top		= bodyPos.top - marginT;
		DragInst.dragdiv.style.left		= bodyPos.left- marginL;

		document.body.appendChild(DragInst.dragdiv);

		DragInst.lastX = e.clientX;
		DragInst.lastY = e.clientY;
		DragInst.lastLeft = parseInt(DragInst.dragdiv.style.left);
		DragInst.lastTop  = parseInt(DragInst.dragdiv.style.top);

		//document.body.style.webkitUserSelect = "none";

		// finalize
		DragInst.dragging = true;

		event.preventDefault();
	}

	this.Moving = function(event)
	{
		var e = event ? event : (window.event ? window.event : null);
		if(e == null) return;

		//output(e.srcElement.className);

		var processDragging = 
			(	DragInst.dragging == true &&
				DragInst.draggable!= null &&
				DragInst.dragdiv  != null );

		if(!processDragging) return;
		if(e.which != 1)	// user might lose focus during dragging
		{
			DragInst.End();
			return;
		}

		var bodyPos = GetBodyPos(e.toElement);
		var bodyX = e.offsetX + bodyPos.left;
		var bodyY = e.offsetY + bodyPos.top;
//		if(DragInst.regDroppable.test(e.toElement.className))
		var focused = false;
		for(var i = 0; i < DragInst.droppableTable.length; i++)
		{
			var tobj = DragInst.droppableTable[i];
			if(tobj.test(bodyX, bodyY) &&
				DragInst.FilterNeighborhood(DragInst.draggable, tobj.droppable))
			{
				SelectionInst.FocusDroppable(tobj.droppable);
				focused = true;
				break;
			}
		}
		if(!focused)
			SelectionInst.FocusDroppable(null);

		DragInst.lastLeft = DragInst.lastLeft + (e.clientX - DragInst.lastX);
		DragInst.lastTop  = DragInst.lastTop  + (e.clientY - DragInst.lastY);
		//DragInst.dragdiv.style.left = DragInst.lastLeft;
		DragInst.dragdiv.style.top  = DragInst.lastTop;

		DragInst.lastX = e.clientX;
		DragInst.lastY = e.clientY;

		event.preventDefault();
		var strPos = "Moving: " + e.x + ", " + e.y;
		//output(strPos);
	}

	this.End = function(e)
	{
		if(!DragInst.dragging) return;

		// re-organize
		if(SelectionInst.curDroppable != null)
		{
			var dragBlock = DragInst.draggable.parentNode;
			var dropBlock = SelectionInst.curDroppable.parentNode;
			var page = dropBlock.parentNode;
			dragBlock = page.removeChild(dragBlock);
			if(dragBlock != null)
			{
				page.insertBefore(dragBlock, dropBlock);
			}
			
			DragInst.RefreshDroppableTable();
		}

		// finalize
		DragInst.dragging = false;
		if(DragInst.dragdiv != null)
		{
			document.body.removeChild(DragInst.dragdiv);
			DragInst.dragdiv = null;
		}
		DragInst.draggable = null;

		SelectionInst.FocusDroppable(null);
	}
}

function initDragHandler()
{
	if(DragInst != null)
		return;

	DragInst = new DragHandler();
	DragInst.Init();
}